using System;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.Practices.ServiceLocation;

namespace Caramel
{
	public static class Repository<T> where T : class
	{
		public static IRepository<T> Current
		{
			get
			{
				var repository =
					ServiceLocator.
						Current.
						TryGetInstance<IRepository<T>>();

				if (repository == null)
				{
					throw new NullReferenceException("Repository for type '{0}' could not be found.".FormatWith(typeof(T)));
				}

				return repository;
			}
		}

		public static T Get(object id)
		{
			Func<T> get = 
				() => 
				Repository<T>.
					Current.
					Get(
						id
					);

			if (UnitOfWork.Current == null)
			{
				return UnitOfWork.ReadOnly<T>(get);
			}

			return get();
		}

		public static T Save(T entity)
		{
			Action save = 
				() =>
				Repository<T>.
					Current.
					Save(
						entity
					);

			if (UnitOfWork.Current == null)
			{
				UnitOfWork.Commit(
					() =>
					save()
				);
			}
			else
			{
				save();
			}

			return entity;
		}

		/// <summary>
		/// Deletes the specified entity from the repository.
		/// </summary>
		/// <param name="entity">The entity to delete.</param>
		public static void Delete(T entity)
		{
			Action delete =
				() =>
				Repository<T>.
					Current.
					Delete(
						entity
					);

			if (UnitOfWork.Current == null)
			{
				UnitOfWork.Commit(
					() =>
					delete()
				);
			}
			else
			{
				delete();
			}
		}

		public static void Delete(object id)
		{
			Action delete =
				() =>
				Repository<T>.
					Current.
					Delete(
						id
					);

			if (UnitOfWork.Current == null)
			{
				UnitOfWork.Commit(
					() =>
					delete()
				);
			}
			else
			{
				delete();
			}
		}

		/// <summary>
		/// Deletes all entities of T from the repository.
		/// </summary>
		public static void DeleteAll()
		{
			Action deleteAll =
				() =>
				Repository<T>.
					Current.
					DeleteAll();

			if (UnitOfWork.Current == null)
			{
				if (UnitOfWork.Current == null)
				{
					UnitOfWork.Commit(
						() =>
						deleteAll()
					);
				}
			}
			else
			{
				deleteAll();
			}
		}

		public static void DeleteAll(Expression<Func<T, bool>> predicate)
		{
			Action deleteAll =
				() =>
				Repository<T>.
					Current.
					DeleteAll(
						predicate
					);

			if (UnitOfWork.Current == null)
			{
				UnitOfWork.Commit(
					() =>
					deleteAll()
				);
			}
			else
			{
				deleteAll();
			}
		}

		public static IQueryable<T> All
		{
			get
			{
				return
					Repository<T>.Current.All;
			}
		}
	}
}
